<html>
    <head>
        <title>小部件</title>
    </head>
    <body>
        <script>
            // 小部件  Widgets  

                   /*
                            ABP提供了一个模型和基础架构来创建"可重用小部件"。  

                            小部件系统是ASP.NETCore的ViewComponents的扩展。

                            小部件在您需要时特别有用：

                            
                                ①、小部件可以包含脚本 和 样式依赖

                                ②、使用小部件创建仪表板  

                                ③、在可重用模块中定义小部件

                                ④、与授权和捆绑系统合作使用小部件。
                   */

                   /*
                        一、定义基础小部件

                                1、创建视图组件   Create a View Component

                                            第一步，创建一个新的常规ASP.NET Core 视图组件 ViewComponent：


                                                            MySimpleWidgetViewComponent. cs：

                                                                    using Microsoft.AspNetCore.Mvc;
                                                                    using Volo.Abp.AspNetCore.Mvc;

                                                                    namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
                                                                    {
                                                                        public class MySimpleWidgetViewComponent : AbpViewComponent  // 继承自 AbpViewComponent 而不是 ViewComponent
                                                                        {
                                                                            public IViewComponentResult Invoke()
                                                                            {
                                                                                return View();  // 返回默认组件视图 Default component view
                                                                            }
                                                                        }
                                                                    }

                                                            不需要从AbpViewComponent继承。您可以从ASP.NETCore的标准ViewComponent继承。AbpViewComponent仅定义了一些基本有用的属性。

                                                            您可以注入服务并在Invoke方法中使用以从服务中获取一些数据。
                                                            
                                                            您可能需要使Invoke方法异步，如public async Task<IViewComponentResult> InvokeAsync()。

                                                            请参阅ASP.NETCore的ViewComponents文档中所有不同的用法: 
                                                            
                                                            https://learn.microsoft.com/zh-cn/aspnet/core/mvc/views/view-components?view=aspnetcore-9.0

                                                            Default.cshtml:

                                                                        <div class="my-simple-widget">
                                                                            <h2>My Simple Widget</h2>
                                                                            <p>This is a simple widget!</p>
                                                                        </div>

                                2、定义小部件  Define the Widget

                                            将[Widget]特性添加到MySimpleWidgetViewComponent类以将此视图组件标记为小部件：

                                                        using Microsoft.AspNetCore.Mvc;
                                                        using Volo.Abp.AspNetCore.Mvc;
                                                        using Volo.Abp.AspNetCore.Mvc.UI.Widgets;

                                                        namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
                                                        {
                                                            [Widget]
                                                            public class MySimpleWidgetViewComponent : AbpViewComponent
                                                            {
                                                                public IViewComponentResult Invoke()
                                                                {
                                                                    return View();
                                                                }
                                                            }
                                                        }    
                                                            
                   */


                   /*
                       二、渲染小部件  Render the Widget

                                    渲染小部件是非常标准的。在razor视图/页面中使用Component.InvokeAsync方法，就像您对任何视图组件所做的那样。

                                    eg:
                                         @await Component.InvokeAsync("MySimpleWidget")
                                         @await Component.InvokeAsync(typeof(MySimpleWidgetViewComponent))

                                    第一种方法使用小部件名称，而第二种方法使用视图组件类型。


                                    1、带参数的小部件   Widgets with Parameters

                                            ASP.NETCore的视图组件系统允许您接受视图组件的参数。

                                            下面的示例视图组件接受startDate和endDate并使用这些参数从服务中检索数据。

                                                    using System;
                                                    using System.Threading.Tasks;
                                                    using Microsoft.AspNetCore.Mvc;
                                                    using Volo.Abp.AspNetCore.Mvc;
                                                    using Volo.Abp.AspNetCore.Mvc.UI.Widgets;

                                                    namespace DashboardDemo.Web.Pages.Shared.Components.CountersWidget
                                                    {
                                                        [Widget]
                                                        public class CountersWidgetViewComponent : AbpViewComponent
                                                        {
                                                            private readonly IDashboardAppService _dashboardAppService;

                                                            public CountersWidgetViewComponent(IDashboardAppService dashboardAppService)
                                                            {
                                                                _dashboardAppService = dashboardAppService;
                                                            }

                                                            public async Task<IViewComponentResult> InvokeAsync(
                                                                DateTime startDate, DateTime endDate)
                                                            {
                                                                var result = await _dashboardAppService.GetCountersWidgetAsync(
                                                                    new CountersWidgetInputDto
                                                                    {
                                                                        StartDate = startDate,
                                                                        EndDate = endDate
                                                                    }
                                                                );

                                                                return View(result);
                                                            }
                                                        }
                                                    }

                                           现在，您需要传递一个匿名对象来传递参数，如下所示：

                                                    @await Component.InvokeAsync("CountersWidget", new
                                                    {
                                                        startDate = DateTime.Now.Subtract(TimeSpan.FromDays(7)),
                                                        endDate = DateTime.Now
                                                    })    
                                           
                   */

                   /*
                        三、小部件名称  Widget Name


                                1、如何声明小部件的名称

                                                视图组件的默认名称是根据视图组件类型的名称计算的。  

                                                如果您的视图组件类型是MySimpleWidgetViewComponent，则小部件名称将是MySimpleWidget（删除ViewComponent后缀）。

                                                这就是ASP.NETCore计算视图组件名称的方式。

                                                要自定义小部件的名称，只需使用ASP.NETCore的标准[ViewComponent]特性：

                                                        using Microsoft.AspNetCore.Mvc;
                                                        using Volo.Abp.AspNetCore.Mvc;
                                                        using Volo.Abp.AspNetCore.Mvc.UI.Widgets;

                                                        namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
                                                        {
                                                            [Widget]  // 声明视图组件为小部件
                                                            [ViewComponent(Name = "MyCustomNamedWidget")]  // 声明小部件的名称
                                                            public class MySimpleWidgetViewComponent : AbpViewComponent
                                                            {
                                                                public IViewComponentResult Invoke()
                                                                {
                                                                    return View("~/Pages/Components/MySimpleWidget/Default.cshtml");
                                                                }
                                                            }
                                                        }

                                                ABP将通过处理小部件来尊重自定义名称。


                                2、显示名称  Display Name

                                                您还可以为小部件定义人类可读的、可本地化的显示名称。

                                                然后，需要时可以在UI上使用此显示名称。显示名称是可选的，可以使用Widget属性的属性定义：

                                                        using DashboardDemo.Localization;
                                                        using Microsoft.AspNetCore.Mvc;
                                                        using Volo.Abp.AspNetCore.Mvc;
                                                        using Volo.Abp.AspNetCore.Mvc.UI.Widgets;

                                                        namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
                                                        {
                                                            [Widget(
                                                                DisplayName = "MySimpleWidgetDisplayName", // 显示名称对应的本地化键
                                                                DisplayNameResource = typeof(DashboardDemoResource) // 指定本地化资源

                                                                )]
                                                            public class MySimpleWidgetViewComponent : AbpViewComponent
                                                            {
                                                                public IViewComponentResult Invoke()
                                                                {
                                                                    return View();
                                                                }
                                                            }
                                                        }

                                                请参阅本地化文档以了解本地化资源和本地化键。
                   */



                   /*
                        四、样式 和 脚本依赖   Style and Script Dependencies

                                       当您的小部件具有脚本和样式文件时会遇到一些挑战：
                                       
                                               ①、任何使用小部件的页面还应该将其脚本和样式文件包含到页面内。
                                               
                                               ②、该页面还应该关心小部件的依赖库/文件

                                        当您将资源与小部件正确关联时，ABP可以解决这些问题。
                                        
                                        您在使用小部件时不用关心小部件的依赖关系。

                                        1、将依赖定义为简单文件路径 Defining as Simple File Paths

                                                    下面的示例小部件添加了样式和脚本文件：

                                                            using Microsoft.AspNetCore.Mvc;
                                                            using Volo.Abp.AspNetCore.Mvc;
                                                            using Volo.Abp.AspNetCore.Mvc.UI.Widgets;

                                                            namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
                                                            {
                                                                [Widget(
                                                                    StyleFiles = new[] { "/Pages/Components/MySimpleWidget/Default.css" },
                                                                    ScriptFiles = new[] { "/Pages/Components/MySimpleWidget/Default.js" }
                                                                    )]  // 将依赖定义为简单的文件路径
                                                                public class MySimpleWidgetViewComponent : AbpViewComponent
                                                                {
                                                                    public IViewComponentResult Invoke()
                                                                    {
                                                                        return View();
                                                                    }
                                                                }
                                                            }

                                                    ABP会考虑这些依赖关系，并在您使用小部件时正确添加到视图/页面。
                                                    
                                                    样式/脚本文件可以是物理或虚拟。它完全集成到虚拟文件系统。


                                        2、将依赖定义为捆绑包贡献者   Defining Bundle Contributors 

                                                    页面中使用的小部件的所有资源都作为bundle添加（如果您不进行其他配置，则在生产环境中打包和压缩）。

                                                    除了添加一个简单的文件，您还可以充分利用捆绑贡献者的力量。

                                                    下面的示例代码与上面的代码相同，但定义和使用捆绑包贡献者：

                                                                using System.Collections.Generic;
                                                                using Microsoft.AspNetCore.Mvc;
                                                                using Volo.Abp.AspNetCore.Mvc;
                                                                using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
                                                                using Volo.Abp.AspNetCore.Mvc.UI.Widgets;

                                                                namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
                                                                {
                                                                    [Widget(
                                                                        StyleTypes = new []{ typeof(MySimpleWidgetStyleBundleContributor) },
                                                                        ScriptTypes = new[]{ typeof(MySimpleWidgetScriptBundleContributor) }
                                                                        )] // 依赖声明为捆绑包贡献者
                                                                    public class MySimpleWidgetViewComponent : AbpViewComponent
                                                                    {
                                                                        public IViewComponentResult Invoke()
                                                                        {
                                                                            return View();
                                                                        }
                                                                    }

                                                                    public class MySimpleWidgetStyleBundleContributor : BundleContributor
                                                                    {
                                                                        public override void ConfigureBundle(BundleConfigurationContext context)
                                                                        {
                                                                            context.Files
                                                                            .AddIfNotContains("/Pages/Components/MySimpleWidget/Default.css");
                                                                        }
                                                                    }

                                                                    public class MySimpleWidgetScriptBundleContributor : BundleContributor
                                                                    {
                                                                        public override void ConfigureBundle(BundleConfigurationContext context)
                                                                        {
                                                                            context.Files
                                                                            .AddIfNotContains("/Pages/Components/MySimpleWidget/Default.js");
                                                                        }
                                                                    }
                                                                }
                                                    捆绑贡献系统非常强大。

                                                    如果你的小部件使用JavaScript库来呈现图表，那么你可以将其声明为依赖项，因此如果之前没有添加JavaScript库，它会自动添加到页面中。

                                                    这样，使用你的小部件的页面就不会关心依赖项。
                   */

                   /*
                        五、用来刷新小部件的Url  RefreshUrl 

                                        小部件可以设计一个RefreshUrl,  每当小部件需要刷新时使用它。
                                        
                                        如果定义了，则小部件在每次刷新时都会在服务器端重新呈现（请参阅下面WidgetManager的刷新method）。

                                                    [Widget(RefreshUrl = "Widgets/Counters")]  // 声明用于刷新小部件的url，需要为此url配置端点
                                                    public class CountersWidgetViewComponent : AbpViewComponent
                                                    {
                                                        
                                                    }

                                        一旦你为你的小部件定义了一个RefreshUrl，你需要提供一个端点来渲染和返回它：

                                                    [Route("Widgets")]
                                                    public class CountersWidgetController : AbpController
                                                    {
                                                        [HttpGet]  // 必须为GET请求
                                                        [Route("Counters")] // 必须与RefreshUrl匹配
                                                        public IActionResult Counters(DateTime startDate, DateTime endDate)
                                                        {
                                                            return ViewComponent("CountersWidget", new {startDate, endDate}); // 返回新的视图组件实例，并传递参数给视图组件的构造函数
                                                        }
                                                    }
                                                    
                                                    Widgets/Counters路由匹配到之前声明的RefreshUrl

                                        ★、应该通过两种方式刷新小部件：

                                                    ①、第一种方式，当你使用 RefreshUrl时，它会在服务器上重新渲染，并且服务器返回的html替换。

                                                    ②、第二种方式，小部件从服务器货期数据(通常是JSON)，并在客户端实现自己。(请参阅小部件JavaScript API部分中的刷新方法)
                   */


                   /*
                        六、自动初始化   AutoInitialize

                                        [Widget]特性有一个 "AutoInitialize" 属性（bool）,可以设置为在页面准备就绪&每当小部件添加到DOM时自动初始化小部件。默认值为false。

                                        如果小部件配置为自动初始化，则会自动为该小部件的实例创建和初始化WidgetManager（见下文）。

                                        这在小部件实例未分组且单独工作时很有用（它们不需要一起初始化或刷新）。

                                        将AutoInitialize设置为true相当于自己编写这样的代码：

                                                  // js代码，初始化小部件
                                                  $('.abp-widget-wrapper[data-widget-name="MySimpleWidget"]')
                                                        .each(function () {
                                                            var widgetManager = new abp.WidgetManager({
                                                                wrapper: $(this),
                                                            });

                                                            widgetManager.init($(this));
                                                        });
                                                    
                                        AutoInitialize还支持通过AJAX加载/刷新的小部件（稍后添加到DOM中）和/或以嵌套方式使用（另一个小部件中的小部件）。
                                        
                                        如果你不需要对多个小部件进行分组并使用单个WidgetManager进行控制，AutoInitialize是推荐的方法。
                   */

                   /*
                        七、 JavaScript Api：

                                        可能需要在客户端呈现和刷新小部件。

                                        在这种情况下，您可以使用ABP的WidgetManager并为您的小部件定义API。


                                    1、WidgetManager 小部件管理器

                                               WidgetManager用于"初始化" 和 "刷新" 一个或多个小部件。 
                                               
                                               创建一个新的WidgetManager如下所示：

                                                         $(function() {
                                                            var myWidgetManager = new abp.WidgetManager('#MyDashboardWidgetsArea');    
                                                         })   

                                                注：  
                                                      MyDashboardWidgetsArea（css选择器）可能包含一个或多个小部件。

                                                      在document. ready（如上）中使用WidgetManager是一种很好的做法，因为它的函数使用DOM并且需要DOM准备就绪。


                                                (1)、WidgetsManager.init() 方法

                                                            init简单地初始化WidgetManager并调用相关小部件的init方法（如果它们定义）（请参阅下面的Widget JavaScript API部分）

                                                                    myWidgetManager.init();

                                                (2)、WidgetsManager.refresh() 方法

                                                            refresh方法刷新与此WidgetManager相关的所有小部件：

                                                                    myWidgetManager.refresh();

                                                (3)、WidgetManager 的可设置的选项

                                                             WidgetManager有一些额外的选项。

                                                             ①、Filter Form 过滤表格

                                                                    如果您的小部件需要参数/过滤器，那么您通常会有一个表单来过滤小部件.

                                                                    在这种情况下，您可以创建一个包含一些表单元素的表单和一个包含一些小部件的仪表板区域。

                                                                                <form method="get" id="MyDashboardFilterForm">
                                                                                    ...form elements
                                                                                </form>

                                                                                <div id="MyDashboardWidgetsArea" data-widget-filter="#MyDashboardFilterForm">
                                                                                    ...widgets
                                                                                </div>

                                                                    data-widget-filter属性将表单与小部件相关联。

                                                                    每当提交表单时，所有小部件都会自动使用表单字段作为过滤器刷新。

                                                                    您可以使用WidgetManager构造函数的filterForm参数来代替data-widget-filter属性。
                                                                    
                                                                    例子：

                                                                            var myWidgetManager = new abp.WidgetManager({
                                                                                wrapper: '#MyDashboardWidgetsArea',
                                                                                filterForm: '#MyDashboardFilterForm'
                                                                            });

                                                             ②、过滤器回调

                                                                     您可能希望有一个更好的控件来在初始化和刷新小部件时提供过滤器。
                                                                     
                                                                     在这种情况下，您可以使用filterCallback选项：

                                                                             var myWidgetManager = new abp.WidgetManager({
                                                                                wrapper: '#MyDashboardWidgetsArea',
                                                                                filterCallback: function() {
                                                                                    return $('#MyDashboardFilterForm').serializeFormToObject();
                                                                                }
                                                                            }); 
                                                                            
                                                                     此示例显示了filterCallback的默认实现。您可以返回任何带有字段的JavaScript对象。
                                                                     
                                                                     eg：
                                                                                
                                                                                filterCallback: function() {
                                                                                    return {
                                                                                        'startDate': $('#StartDateInput').val(),
                                                                                        'endDate': $('#EndDateInput').val()
                                                                                    };
                                                                                }

                                                                    返回的过滤器被传递给init和refresh上的所有小部件。


                                            2、小部件 JavaScript Api      Widget JavaScript API 
                                    
                                                        小部件可以定义一个JavaScript API，  该API在需要时由WidgetManager调用。

                                                        下面的代码示例可用于开始为小部件定义API。

                                                                    (function () {
                                                                        abp.widgets.NewUserStatisticWidget = function ($wrapper) {  // NewUserStatisticWidget 是小部件名称

                                                                            var getFilters = function () {
                                                                                return {
                                                                                    ...
                                                                                };
                                                                            }

                                                                            var refresh = function (filters) {
                                                                                ...
                                                                            };

                                                                            var init = function (filters) {
                                                                                ...
                                                                            };

                                                                            return {
                                                                                getFilters: getFilters,
                                                                                init: init,
                                                                                refresh: refresh
                                                                            };
                                                                        };
                                                                    })();

                                                        NewUserStatisticWidget是这里小部件的名称。
                                                        
                                                        它应该与服务器端定义的小部件名称匹配。所有功能都是可选的。



                                                        (1)、getFilters

                                                                如果小部件具有内部自定义过滤器，则此函数应返回过滤器对象。
                                                                
                                                                eg：
                                                                        var getFilters = function() {
                                                                            return {
                                                                                frequency: $wrapper.find('.frequency-filter option:selected').val()
                                                                            };
                                                                        }

                                                                    WidgetManager在构建过滤器时使用此方法。

                                                        (2)、init 方法

                                                                用于在需要时初始化小部件。 它有一个过滤器参数，可以在从服务器获取数据时使用。

                                                                init方法在调用WidgetManager.init()函数时使用。

                                                                如果您的小部件需要在刷新时完全重新加载，也会调用它。请参阅RefreshUrl小部件选项。



                                                        (3)、refresh 方法

                                                                    用于在需要时刷新小部件。
                                                                    
                                                                    它有一个过滤器参数，可在从服务器获取数据时使用。
                                                                    
                                                                    每当调用WidgetManager.refresh()函数时，都会使用refresh方法。
                   */

                  

                   /*
                        八、授权  Authorization

                                    某些小部件可能需要仅对经过身份验证或授权的用户可用。
                                    
                                    在这种情况下，请使用Widget属性的以下属性：

                                            ①、RequiresAuthentication（bool）：设置为true以使此小部件仅可用于身份验证用户（用户已登录到应用程序）。

                                            ②、RequiredPolicies（List<string>）：授权用户的策略名称列表。有关策略的更多信息，请参阅授权文档:

                                                        https://abp.io/docs/latest/framework/fundamentals/authorization

                                    eg:

                                            using Microsoft.AspNetCore.Mvc;
                                            using Volo.Abp.AspNetCore.Mvc;
                                            using Volo.Abp.AspNetCore.Mvc.UI.Widgets;

                                            namespace DashboardDemo.Web.Pages.Components.MySimpleWidget
                                            {
                                                [Widget(RequiredPolicies = new[] { "MyPolicyName" })]  // 需要授权策略
                                                public class MySimpleWidgetViewComponent : AbpViewComponent
                                                {
                                                    public IViewComponentResult Invoke()
                                                    {
                                                        return View();
                                                    }
                                                }
                                            }
                   */

                   /*
                        九、小部件选项类  WidgetOptions选项类

                                    作为Widget属性的替代方法，您可以使用AbpWidgetOptions来配置小部件：

                                            Configure<AbpWidgetOptions>(options =>
                                            {
                                                options.Widgets.Add<MySimpleWidgetViewComponent>();
                                            });   

                                    将其写入模块的ConfigureServices方法中。

                                    使用Widget属性完成的所有配置也可以使用AbpWidgetOptions。

                                    为小部件添加样式的示例配置：

                                            Configure<AbpWidgetOptions>(options =>
                                            {
                                                options.Widgets
                                                    .Add<MySimpleWidgetViewComponent>()
                                                    .WithStyles("/Pages/Components/MySimpleWidget/Default.css");
                                            });  
                                            
                                            
                                    提示：AbpWidgetOptions也可用于获取现有小部件并更改其配置。
                                    
                                          如果您想修改应用程序使用的模块内小部件的配置，这尤其有用。使用options.Widgets.Find获取现有的WidgetDefinition。
                   */
        </script>
    </body>
</html>